import scss from "@/app/[language]/site/components/SiteYellView.module.scss"; import wsAPI from "@/app/[language]/site/lib/wsAPI"; import { AbilitySiteYell, CommentSiteYell, InviteSiteYell, LevelSiteYell, OnSiteYellInput, SiteYellItem, TVSiteYell, } from "@/app/[language]/site/type"; import tv0 from "@/assets/tv0.svg"; import tv1 from "@/assets/tv1.png"; import AvatarDrawing from "@/components/AvatarDrawing"; import AvatarTitle from "@/components/AvatarTitle"; import HitPointsModeText from "@/components/HitPointsModeText"; import LevelText from "@/components/LevelText"; import { useSiteStore } from "@/state/Stores"; import { formatText, getAbilityUpText, getGenreText, } from "@/utilities/Utility"; import { sanitize } from "isomorphic-dompurify"; import { observer } from "mobx-react-lite"; import { useTranslations } from "next-intl"; import Image from "next/image"; import Link from "next/link"; import Col from "react-bootstrap/Col"; import Row from "react-bootstrap/Row"; import Stack from "react-bootstrap/Stack"; const EventPB = require("@/Event_pb"); export default observer( ({ siteYellItem, onSiteYellInput, }: { siteYellItem: SiteYellItem; onSiteYellInput: OnSiteYellInput; }) => { const { siteYellVariety, date, avatarID, avatarName, siteYell } = siteYellItem; const { saveTraffic } = useSiteStore(); const t = useTranslations(); const doSiteYell = (siteYell: string) => { siteYell = siteYell.replace(" ", " "); const m = siteYell.match(/(http|https|mailto|rtmp):\/\/[^ ]+/i); if (m != null) { const t = m[0]; if ( !saveTraffic && t.match(/(\.aac|\.flac|\.mid|\.midi|\.mp3|\.ogg|\.wav|\.wma)($|\?)/i) ) { return siteYell.replace( t, `<audio class="${scss.siteYell}" src="${t}" controls></audio>`, ); } if ( !saveTraffic && t.match(/(\.bmp|\.gif|\.heic|\.jpeg|\.jpg|\.png|\.webp)($|\?)/i) ) { return siteYell.replace( t, `<a href="${t}" target="_blank"> <img class="${scss.siteYell} rounded border" src="${t}" alt=""> </a>`, ); } if ( !saveTraffic && t.match(/(\.avi|\.mkv|\.mp4|\.mpeg|\.mpg|\.wmv)($|\?)/i) ) { return siteYell.replace( t, `<video class="${scss.siteYell}" src="${t}" controls></video>`, ); } return siteYell.replace(t, `<a href="${t}" target="_blank">${t}</a>`); } return siteYell; }; switch (siteYellVariety) { case "@Enter": return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <span className={`ellipsis ${scss.enter}`}> {t("siteYellEnter")} </span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); case "@Quit": return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <span className={`ellipsis ${scss.quit}`}> {t("siteYellQuit")} </span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); case "@Site": return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <span className={`ellipsis ${scss.enter}`}> {t("siteYellNewSite")} </span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); case "@Net": return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <span className={`ellipsis ${scss.enter}`}> {t("siteYellNewNetSite")} </span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); case "@Notify": return ( <Row className="flex-nowrap"> <Col className="cc"> <Stack gap={2}> <span>{t("siteYellTaehui")}</span> <span dangerouslySetInnerHTML={{ __html: doSiteYell(siteYell as string), }} /> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); case "@Invite": { const { siteID, siteName, avatarName } = siteYell as InviteSiteYell; return ( <Row className="rc route" onClick={() => { wsAPI.send({ eventID: EventPB.Event.EventID.ENTER_SITE, text: JSON.stringify({ siteID, siteCipher: "", }), }); }} onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <span className="ellipsis"> {t("siteYellInvite", { siteName })} </span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); } case "@TV": const { href, title, text } = siteYell as TVSiteYell; return ( <Row className="flex-nowrap" as={Link} href={href} onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > <Col xs="auto"> {href.startsWith("https://www.twitch.tv") && ( <Image src={tv0} alt="" width={60} height={60} /> )} {href.startsWith("https://chzzk.naver.com") && ( <Image src={tv1} alt="" width={60} height={60} /> )} </Col> <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={text} className="ellipsis" /> <span className="ellipsis">{t("siteYellTV", { title })}</span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); case "@Wiped": return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <span className="ellipsis">{t("wipedSiteYell")}</span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); case "@Comment": { const { avatarID, avatarName, title, artist, genre, level, levelText, stand, hitPointsMode, } = siteYell as CommentSiteYell; return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <Stack gap={2} direction="horizontal"> <LevelText level={level} levelText={levelText} className="ellipsis" /> <span className="ellipsis">{title}</span> </Stack> <Stack gap={2} direction="horizontal"> <span className="ellipsis artist">{artist}</span> <span className="ellipsis genre">{getGenreText(genre)}</span> </Stack> </Stack> </Col> <Col xs="auto"> <Stack gap={2} className="text-end"> <span className="date">{date}</span> <HitPointsModeText hitPointsMode={hitPointsMode} text={t("textStand", { value: formatText(stand) })} /> </Stack> </Col> </Row> ); } case "@Ability": { const { avatarID, avatarName } = siteYell as AbilitySiteYell; return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <span className="ellipsis"> {getAbilityUpText(t, siteYell as AbilitySiteYell)} </span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); } case "@Level": { const { avatarID, avatarName, title } = siteYell as LevelSiteYell; return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> <span className="ellipsis"> {t("wwwLevelClearText", { title })} </span> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); } case "": return <>{siteYell}</>; case null: return ( <Row className="flex-nowrap" onContextMenu={(e) => { e.preventDefault(); if (avatarID) { onSiteYellInput(e, avatarID); } }} > {avatarID && ( <Col xs="auto"> <AvatarDrawing avatarID={avatarID} /> </Col> )} <Col className="cc"> <Stack gap={2}> <Stack gap={2} direction="horizontal"> <AvatarTitle avatarID={avatarID} avatarName={avatarName} className="ellipsis" /> </Stack> <span className="ellipsis" dangerouslySetInnerHTML={{ __html: doSiteYell(sanitize(siteYell as string)), }} /> </Stack> </Col> <Col xs="auto"> <span className="date">{date}</span> </Col> </Row> ); default: return null; } }, );